//Copyright (c) 2006-2007 Microsoft Corporation.  All rights reserved
using System;
using System.Threading;
using System.Xml;

using InvocationFramework.Common;

namespace InvocationFramework.Core
{
    /// <summary>
    /// Esta clase permite ejecutar servicios mediante los adapters configurados (<seealso cref="InvocationFramework.Common.Interfaces.IServiceAdapter"/>)
    /// </summary>
    /// <remarks>
    /// Esta clase es un fachada a todo la infraestructura de Invocation Framework para ser utilizada por los clientes.
    /// Una descripcion del patron Facade se encuentra en http://www.dofactory.com/Patterns/PatternFacade.aspx
    /// </remarks>
    /// <author>Rodolfo Finochietti 02/11/2005 10.00</author>
    public static class ServiceCaller
    {
        #region Private Vars

        /// <summary>
        /// Singleton para manejar una sola instancia del broker
        /// </summary>
        private readonly static ServiceBridge _instance;

        #endregion

        #region Properties

        /// <summary>
        /// Variable privada para el manejo de la propiedad
        /// </summary>
        private static ContextDictionary _context = new ContextDictionary( );

        /// <summary>
        /// Contexto donde las aplicaciones pueden guadar informacion fuera de banda para que las aplicaciones la transmitan
        /// </summary>
        public static ContextDictionary Context
        {
            get
            {
                return _context;
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Construtor
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline" )]
        static ServiceCaller( )
        {
            //Creo el singleton del broker
            _instance = new ServiceBridge( );
            
            //Creo la configuracion que indica que el broker debe cargar todos los adaptadores y luego inicializo el broker con esta configuracion
            XmlDocument config = new XmlDocument( );
            config.LoadXml( "<serviceBridge publisherAdapters='false'/>" );
            _instance.Init( "#ServiceCaller#", config );

            //Libero recursos
            config = null;
        }

        #endregion

        #region Public Methods

        #region Execute
        
        /// <summary>
        /// Ejecuta la operacion que se encuentra en un determinado servicio utilizando alguno de los adapters configurados (<seealso cref="InvocationFramework.Common.Interfaces.IServiceAdapter"/>) 
        /// </summary>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <returns>Respuesta del servicio</returns>
        public static object Execute( string serviceName, string operationName )
        {
            return Execute( serviceName, operationName, null );
        }

        /// <summary>
        /// Ejecuta la operacion que se encuentra en un determinado servicio utilizando alguno de los adapters configurados (<seealso cref="InvocationFramework.Common.Interfaces.IServiceAdapter"/>) 
        /// </summary>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <param name="parameters">Parametros de la operacion</param>
        /// <returns>Respuesta del servicio</returns>
        public static object Execute( string serviceName, string operationName, params object[] parameters )
        {
            return _instance.Execute( serviceName, operationName, _context, parameters );
        }

        /// <summary>
        /// Ejecuta asincronicamente la operacion que se encuentra en un determinado servicio utilizando alguno de los adapters configurados (<seealso cref="InvocationFramework.Common.Interfaces.IServiceAdapter"/>) 
        /// </summary>
        /// <param name="executionCompletedEvent">Delegate que se utilizara para notificar el resultdo de la ejecucion del servicio</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        public static void AsyncExecute( EventHandler<AsyncExecutionCompletedEventArgs> executionCompletedEvent, string serviceName, string operationName )
        {
            AsyncExecute( executionCompletedEvent, serviceName, operationName, null );
        }

        /// <summary>
        /// Ejecuta asincronicamente la operacion que se encuentra en un determinado servicio utilizando alguno de los adapters configurados (<seealso cref="InvocationFramework.Common.Interfaces.IServiceAdapter"/>) 
        /// </summary>
        /// <param name="executionCompletedEvent">Delegate que se utilizara para notificar el resultdo de la ejecucion del servicio</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <param name="parameters">Parametros de la operacion</param>
        public static void AsyncExecute( EventHandler<AsyncExecutionCompletedEventArgs> executionCompletedEvent, string serviceName, string operationName, params object[] parameters )
        {
            //Preparo la informacion de invocacion
            InvocationInformation information = new InvocationInformation( );
            information.AdapterId = String.Empty;
            information.ServiceName = serviceName;
            information.OperationName = operationName;
            information.Context = _context;
            information.Parameters = parameters;

            //Encolo la ejecucion en el thread pool
            ThreadPool.QueueUserWorkItem( new WaitCallback( AsyncExecution ), new AsyncExecutionThreadPoolQueueItem( executionCompletedEvent, null, information ) );
        }

        #endregion        
        
        #region Fix Adapter Execute

        /// <summary>
        /// Ejecuta la operacion que se encuentra en un determinado servicio utilizando el adaptador cuyo id se pasa por parametro
        /// </summary>
        /// <param name="adapterId">Identificador del adaptador que se quiere utilizar para ejecutar la operacion</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <returns>Respuesta del servicio</returns>
        public static object FixAdapterExecute( string adapterId, string serviceName, string operationName )
        {
            return FixAdapterExecute( adapterId, serviceName, operationName, null );
        }

        /// <summary>
        /// Ejecuta la operacion que se encuentra en un determinado servicio utilizando el adaptador cuyo id se pasa por parametro
        /// </summary>
        /// <param name="adapterId">Identificador del adaptador que se quiere utilizar para ejecutar la operacion</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <param name="parameters">Parametros de la operacion</param>
        /// <returns>Respuesta del servicio</returns>
        public static object FixAdapterExecute( string adapterId, string serviceName, string operationName, params object[] parameters )
        {
            return _instance.FixAdapterExecute( adapterId, serviceName, operationName, _context, parameters );
        }

        /// <summary>
        /// Ejecuta asincronicamente la operacion que se encuentra en un determinado servicio el adaptador cuyo id se pasa por parametro
        /// </summary>
        /// <param name="executionCompletedEvent">Delegate que se utilizara para notificar el resultdo de la ejecucion del servicio</param>
        /// <param name="adapterId">Identificador del adaptador que se quiere utilizar para ejecutar la operacion</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        public static void AsyncFixAdapterExecute( EventHandler<AsyncExecutionCompletedEventArgs> executionCompletedEvent, string adapterId, string serviceName, string operationName )
        {
            AsyncExecute( executionCompletedEvent, adapterId, serviceName, operationName, null );
        }

        /// <summary>
        /// Ejecuta asincronicamente la operacion que se encuentra en un determinado servicio el adaptador cuyo id se pasa por parametro
        /// </summary>
        /// <param name="executionCompletedEvent">Delegate que se utilizara para notificar el resultdo de la ejecucion del servicio</param>
        /// <param name="adapterId">Identificador del adaptador que se quiere utilizar para ejecutar la operacion</param>
        /// <param name="serviceName">Nombre del servicio a ejecutar</param>
        /// <param name="operationName">Nombre de la operacion a ejecutar</param>
        /// <param name="parameters">Parametros de la operacion</param>
        public static void AsyncFixAdapterExecute( EventHandler<AsyncExecutionCompletedEventArgs> executionCompletedEvent, string adapterId, string serviceName, string operationName, params object[] parameters )
        {
            //Preparo la informacion de invocacion
            InvocationInformation information = new InvocationInformation( );
            information.AdapterId = adapterId;
            information.ServiceName = serviceName;
            information.OperationName = operationName;
            information.Context = _context;
            information.Parameters = parameters;

            //Encolo la ejecucion en el thread pool
            ThreadPool.QueueUserWorkItem( new WaitCallback( AsyncExecution ), new AsyncExecutionThreadPoolQueueItem( executionCompletedEvent, null, information ) );
        }

        #endregion
        
        #endregion

        #region Private Methods

        /// <summary>
        /// Metodo usado por <seealso cref="ThreadPool"/> para ejecutar servicios asincronicamente
        /// </summary>
        /// <param name="stateInfo"></param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes" )]
        private static void AsyncExecution( object stateInfo )
        {
            AsyncExecutionThreadPoolQueueItem eventItem = stateInfo as AsyncExecutionThreadPoolQueueItem;
            
            if ( eventItem != null )
            {
                InvocationInformation information = eventItem.ExecutionInformation;
                AsyncExecutionResult result = AsyncExecutionResult.Failed;

                try
                {
                    if ( String.IsNullOrEmpty( information.AdapterId ) )
                        information.Result = _instance.Execute( information.ServiceName, information.OperationName, information.Context, information.Parameters );
                    else
                        information.Result = _instance.FixAdapterExecute( information.AdapterId, information.ServiceName, information.OperationName, information.Context, information.Parameters );
                    
                    result = AsyncExecutionResult.Ok;
                }
                catch ( Exception ex )
                {
                    information.InvocationException = ex;
                }

                eventItem.ExecutionCompletedEvent( eventItem.Sender, new AsyncExecutionCompletedEventArgs( information, result ) );
            }
        }

        #endregion
    }
}